home *** CD-ROM | disk | FTP | other *** search
/ Practical Algorithms for Image Analysis / Practical Algorithms for Image Analysis.iso / LIBIP / TLC1FCT.C < prev    next >
C/C++ Source or Header  |  1999-09-11  |  38KB  |  1,172 lines

  1. /* 
  2.  * tlc1fct.c
  3.  * 
  4.  * Practical Algorithms for Image Analysis
  5.  * 
  6.  * Copyright (c) 1997, 1998, 1999 MLMSoftwareGroup, LLC
  7.  */
  8.  
  9. /* TLC1FCT:     functions for TLC1 line analysis on top of PCC coding
  10.  *
  11.  *      FUNCTIONS:
  12.  *              TLC1ATTR, TLC1TRACK, TLC1DIRN, TLC1STORE, TLC1TAG, TLC1RID
  13.  */
  14.  
  15. #include <stdio.h>
  16. #include <stdlib.h>
  17. #include <images.h>             /* images information file */
  18. #include "pcc2.h"               /* header file for PCC programs */
  19.  
  20. extern unsigned char *fcCode;   /* code storage */
  21. extern long nByteCode;          /* no. bytes in code storage */
  22.  
  23. /* TLC1ATTR:    function reads code from code storage, and writes
  24.  *            out attributes of decoded fcc
  25.  *                      usage: tlc1attr (&attr, &nAttr)
  26.  *
  27.  *              Note:   There is a little bit of ugly protocol in here
  28.  *                      which needs to be explained.
  29.  *                      The array, <attr[].inOut> contains the number of
  30.  *                      branches leaving a node... sort of. That is,
  31.  *                      for each branch leaving a node, +1 is added
  32.  *                      to that variable; if a branch goes into that
  33.  *                      node, +10 is added. (There can be only 0 or 
  34.  *                      1 branches into a node.) The variable is 
  35.  *                      associated with the FIRST node of a line
  36.  *                      feature (e.g. the feature, CB has 1 entering
  37.  *                      the first node, and 3 leaving the first node,
  38.  *                      therefore the variable =10 + 3 = 13.)  For the
  39.  *                      attr[] array, the FIRST OUTGOING branch of each
  40.  *                      junction carries information of the junction.
  41.  *                      For example, {EB, BE, BE} have stored inOut,
  42.  *                      {1, 12, 0}. 
  43.  *
  44.  */
  45.  
  46. tlc1attr (attr, nAttr)
  47.      struct attributes **attr;  /* level 1 attributes */
  48.      long *nAttr;               /* no. line structures in attr. array */
  49. {
  50.   register long iByteCode,      /* code storage incrementor */
  51.     codeWord;                   /* code word contains up to 3 dirn.s */
  52.   long xTrack, yTrack;          /* decode tracking  coord.s */
  53.   struct branch1 *branch;       /* stack of branches */
  54.   struct branch1 branchTop;     /* first branch */
  55.   long nBranch;                 /* no. of branches in stack */
  56.   struct attrVar attrVar;       /* variables to calculate level 1 attr.s */
  57.  
  58. /* allocate memory for level 1 attributes and begin linked list of branches */
  59.   if ((*attr = (struct attributes *)
  60.        calloc (nByteCode, sizeof (struct attributes))) == NULL) {
  61.     printf ("TLC1ATTR: CALLOC -- not enough memory\n");
  62.     return (-1);
  63.   }
  64.   branch = &branchTop;
  65.   branch->previous = branch;
  66.  
  67.   *nAttr = 0;
  68.   nBranch = 0;
  69.  
  70. /* decode from storage region and compute line attributes */
  71.  
  72.   for (iByteCode = 0; iByteCode < nByteCode;) {
  73.     codeWord = (long) fcCode[iByteCode++];
  74.  
  75.     /* if it is a feature: find start location if break; push branches if
  76.      * junction; pop branches if endpoint */
  77.  
  78.     if (codeWord >= MINFEATCODE) {
  79.  
  80.       switch (codeWord) {
  81.       case BIFCODE:            /* push one more branch */
  82.         pccbranch (&branch, xTrack, yTrack, BIFCODE);
  83.         attrVar.feat2 = BIFCODE;
  84.         attrVar.pt2.x = xTrack;
  85.         attrVar.pt2.y = yTrack;
  86.         nBranch++;
  87.         attrVar.code2 = (long) fcCode[iByteCode - 2];
  88.  
  89. /*                    if (attrVar.length > 0 || attrVar.feat1 >= STARTCODE) */
  90.         tlc1store (attrVar, *attr, nAttr);
  91.         attrVar.iByteCode = iByteCode;
  92.         attrVar.inOut = 12;
  93.         attrVar.feat1 = BIFCODE;
  94.         attrVar.pt1.x = xTrack;
  95.         attrVar.pt1.y = yTrack;
  96.         attrVar.code1 = (long) fcCode[iByteCode];
  97.         attrVar.length = 0;
  98.         attrVar.nPts = attrVar.sumPt.x = attrVar.sumPt.y = 0;
  99.         attrVar.box.min.x = attrVar.box.max.x = xTrack;
  100.         attrVar.box.min.y = attrVar.box.max.y = yTrack;
  101.         attrVar.sumPt.x += xTrack;
  102.         attrVar.sumPt.y += yTrack;
  103.         attrVar.nPts++;
  104.         if (xTrack < attrVar.box.min.x)
  105.           attrVar.box.min.x = xTrack;
  106.         if (yTrack < attrVar.box.min.y)
  107.           attrVar.box.min.y = yTrack;
  108.         if (xTrack > attrVar.box.max.x)
  109.           attrVar.box.max.x = xTrack;
  110.         if (yTrack > attrVar.box.max.y)
  111.           attrVar.box.max.y = yTrack;
  112.         break;
  113.  
  114.       case CROSSCODE:          /* push two more branches */
  115.         pccbranch (&branch, xTrack, yTrack, CROSSCODE);
  116.         attrVar.feat2 = CROSSCODE;
  117.         attrVar.pt2.x = xTrack;
  118.         attrVar.pt2.y = yTrack;
  119.         nBranch++;
  120.         pccbranch (&branch, xTrack, yTrack, CROSSCODE);
  121.         nBranch++;
  122.         attrVar.code2 = (long) fcCode[iByteCode - 2];
  123.  
  124. /*                    if (attrVar.length > 0 || attrVar.feat1 >= STARTCODE) */
  125.         tlc1store (attrVar, *attr, nAttr);
  126.         attrVar.iByteCode = iByteCode;
  127.         attrVar.inOut = 13;
  128.         attrVar.feat1 = CROSSCODE;
  129.         attrVar.pt1.x = xTrack;
  130.         attrVar.pt1.y = yTrack;
  131.         attrVar.code1 = (long) fcCode[iByteCode];
  132.         attrVar.length = 0;
  133.         attrVar.nPts = attrVar.sumPt.x = attrVar.sumPt.y = 0;
  134.         attrVar.box.min.x = attrVar.box.max.x = xTrack;
  135.         attrVar.box.min.y = attrVar.box.max.y = yTrack;
  136.         attrVar.sumPt.x += xTrack;
  137.         attrVar.sumPt.y += yTrack;
  138.         attrVar.nPts++;
  139.         if (xTrack < attrVar.box.min.x)
  140.           attrVar.box.min.x = xTrack;
  141.         if (yTrack < attrVar.box.min.y)
  142.           attrVar.box.min.y = yTrack;
  143.         if (xTrack > attrVar.box.max.x)
  144.           attrVar.box.max.x = xTrack;
  145.         if (yTrack > attrVar.box.max.y)
  146.           attrVar.box.max.y = yTrack;
  147.         break;
  148.  
  149.       case ENDCODE:            /* pop branches */
  150.         attrVar.feat2 = ENDCODE;
  151.         attrVar.pt2.x = xTrack;
  152.         attrVar.pt2.y = yTrack;
  153.         attrVar.code2 = (long) fcCode[iByteCode - 2];
  154.  
  155. /*                    if (attrVar.length > 0 || attrVar.feat1 >= STARTCODE) */
  156.         tlc1store (attrVar, *attr, nAttr);
  157.  
  158.         if (nBranch > 0) {
  159.           nBranch--;
  160.           branch = branch->previous;
  161.           attrVar.iByteCode = iByteCode;
  162.           attrVar.inOut = 0;
  163.           attrVar.feat1 = branch->variable;
  164.           attrVar.pt1.x = xTrack = branch->x;
  165.           attrVar.pt1.y = yTrack = branch->y;
  166.           attrVar.code1 = (long) fcCode[iByteCode];
  167.           attrVar.length = 0;
  168.           attrVar.nPts = attrVar.sumPt.x = attrVar.sumPt.y = 0;
  169.           attrVar.box.min.x = attrVar.box.max.x = xTrack;
  170.           attrVar.box.min.y = attrVar.box.max.y = yTrack;
  171.           attrVar.sumPt.x += xTrack;
  172.           attrVar.sumPt.y += yTrack;
  173.           attrVar.nPts++;
  174.           if (xTrack < attrVar.box.min.x)
  175.             attrVar.box.min.x = xTrack;
  176.           if (yTrack < attrVar.box.min.y)
  177.             attrVar.box.min.y = yTrack;
  178.           if (xTrack > attrVar.box.max.x)
  179.             attrVar.box.max.x = xTrack;
  180.           if (yTrack > attrVar.box.max.y)
  181.             attrVar.box.max.y = yTrack;
  182.         }
  183.         break;
  184.  
  185.       case STARTCODE:
  186.         xTrack = (long) *(fcCode + iByteCode)
  187.           | ((long) *(fcCode + iByteCode + 1) << 8);
  188.         iByteCode += 2;
  189.         yTrack = (long) *(fcCode + iByteCode)
  190.           | ((long) *(fcCode + iByteCode + 1) << 8);
  191.         iByteCode += 2;
  192.  
  193.         attrVar.iByteCode = iByteCode;
  194.         attrVar.inOut = 1;
  195.         attrVar.feat1 = STARTCODE;
  196.         attrVar.pt1.x = xTrack;
  197.         attrVar.pt1.y = yTrack;
  198.         attrVar.code1 = (long) fcCode[iByteCode];
  199.         attrVar.length = 0;
  200.         attrVar.nPts = attrVar.sumPt.x = attrVar.sumPt.y = 0;
  201.         attrVar.box.min.x = attrVar.box.max.x = xTrack;
  202.         attrVar.box.min.y = attrVar.box.max.y = yTrack;
  203.         attrVar.sumPt.x += xTrack;
  204.         attrVar.sumPt.y += yTrack;
  205.         attrVar.nPts++;
  206.         if (xTrack < attrVar.box.min.x)
  207.           attrVar.box.min.x = xTrack;
  208.         if (yTrack < attrVar.box.min.y)
  209.           attrVar.box.min.y = yTrack;
  210.         if (xTrack > attrVar.box.max.x)
  211.           attrVar.box.max.x = xTrack;
  212.         if (yTrack > attrVar.box.max.y)
  213.           attrVar.box.max.y = yTrack;
  214.         break;
  215.  
  216.       case LINEBRCODE:
  217.         xTrack = (long) *(fcCode + iByteCode)
  218.           | ((long) *(fcCode + iByteCode + 1) << 8);
  219.         iByteCode += 2;
  220.         yTrack = (long) *(fcCode + iByteCode)
  221.           | ((long) *(fcCode + iByteCode + 1) << 8);
  222.         iByteCode += 2;
  223.  
  224.         pccbranch (&branch, xTrack, yTrack, LINEBRCODE);
  225.         nBranch++;
  226.         attrVar.iByteCode = iByteCode;
  227.         attrVar.inOut = 2;
  228.         attrVar.feat1 = LINEBRCODE;
  229.         attrVar.pt1.x = xTrack;
  230.         attrVar.pt1.y = yTrack;
  231.         attrVar.code1 = (long) fcCode[iByteCode];
  232.         attrVar.length = 0;
  233.         attrVar.nPts = attrVar.sumPt.x = attrVar.sumPt.y = 0;
  234.         attrVar.box.min.x = attrVar.box.max.x = xTrack;
  235.         attrVar.box.min.y = attrVar.box.max.y = yTrack;
  236.         attrVar.sumPt.x += xTrack;
  237.         attrVar.sumPt.y += yTrack;
  238.         attrVar.nPts++;
  239.         if (xTrack < attrVar.box.min.x)
  240.           attrVar.box.min.x = xTrack;
  241.         if (yTrack < attrVar.box.min.y)
  242.           attrVar.box.min.y = yTrack;
  243.         if (xTrack > attrVar.box.max.x)
  244.           attrVar.box.max.x = xTrack;
  245.         if (yTrack > attrVar.box.max.y)
  246.           attrVar.box.max.y = yTrack;
  247.         break;
  248.  
  249.       case BIFBRCODE:
  250.         xTrack = (long) *(fcCode + iByteCode)
  251.           | ((long) *(fcCode + iByteCode + 1) << 8);
  252.         iByteCode += 2;
  253.         yTrack = (long) *(fcCode + iByteCode)
  254.           | ((long) *(fcCode + iByteCode + 1) << 8);
  255.         iByteCode += 2;
  256.  
  257.         pccbranch (&branch, xTrack, yTrack, BIFBRCODE);
  258.         nBranch++;
  259.         pccbranch (&branch, xTrack, yTrack, BIFBRCODE);
  260.         nBranch++;
  261.         attrVar.iByteCode = iByteCode;
  262.         attrVar.inOut = 3;
  263.         attrVar.feat1 = BIFBRCODE;
  264.         attrVar.pt1.x = xTrack;
  265.         attrVar.pt1.y = yTrack;
  266.         attrVar.code1 = (long) fcCode[iByteCode];
  267.         attrVar.length = 0;
  268.         attrVar.width = attrVar.nWidth = 0;
  269.         attrVar.nPts = attrVar.sumPt.x = attrVar.sumPt.y = 0;
  270.         attrVar.box.min.x = attrVar.box.max.x = xTrack;
  271.         attrVar.box.min.y = attrVar.box.max.y = yTrack;
  272.         attrVar.sumPt.x += xTrack;
  273.         attrVar.sumPt.y += yTrack;
  274.         attrVar.nPts++;
  275.         if (xTrack < attrVar.box.min.x)
  276.           attrVar.box.min.x = xTrack;
  277.         if (yTrack < attrVar.box.min.y)
  278.           attrVar.box.min.y = yTrack;
  279.         if (xTrack > attrVar.box.max.x)
  280.           attrVar.box.max.x = xTrack;
  281.         if (yTrack > attrVar.box.max.y)
  282.           attrVar.box.max.y = yTrack;
  283.         break;
  284.  
  285.       case CROSSBRCODE:
  286.         xTrack = (long) *(fcCode + iByteCode)
  287.           | ((long) *(fcCode + iByteCode + 1) << 8);
  288.         iByteCode += 2;
  289.         yTrack = (long) *(fcCode + iByteCode)
  290.           | ((long) *(fcCode + iByteCode + 1) << 8);
  291.         iByteCode += 2;
  292.  
  293.         pccbranch (&branch, xTrack, yTrack, CROSSBRCODE);
  294.         nBranch++;
  295.         pccbranch (&branch, xTrack, yTrack, CROSSBRCODE);
  296.         nBranch++;
  297.         pccbranch (&branch, xTrack, yTrack, CROSSBRCODE);
  298.         nBranch++;
  299.         attrVar.iByteCode = iByteCode;
  300.         attrVar.inOut = 4;
  301.         attrVar.feat1 = CROSSBRCODE;
  302.         attrVar.pt1.x = xTrack;
  303.         attrVar.pt1.y = yTrack;
  304.         attrVar.code1 = (long) fcCode[iByteCode];
  305.         attrVar.length = 0;
  306.         attrVar.width = attrVar.nWidth = 0;
  307.         attrVar.nPts = attrVar.sumPt.x = attrVar.sumPt.y = 0;
  308.         attrVar.box.min.x = attrVar.box.max.x = xTrack;
  309.         attrVar.box.min.y = attrVar.box.max.y = yTrack;
  310.         attrVar.sumPt.x += xTrack;
  311.         attrVar.sumPt.y += yTrack;
  312.         attrVar.nPts++;
  313.         if (xTrack < attrVar.box.min.x)
  314.           attrVar.box.min.x = xTrack;
  315.         if (yTrack < attrVar.box.min.y)
  316.           attrVar.box.min.y = yTrack;
  317.         if (xTrack > attrVar.box.max.x)
  318.           attrVar.box.max.x = xTrack;
  319.         if (yTrack > attrVar.box.max.y)
  320.           attrVar.box.max.y = yTrack;
  321.         break;
  322.  
  323.       case LINECODE:
  324.         break;
  325.  
  326.       case STOPCODE:
  327.         tlc1dirn (*attr, *nAttr);
  328.         *attr = (struct attributes *) realloc (*attr,
  329.                                        *nAttr * sizeof (struct attributes));
  330.  
  331.         return (0);
  332.  
  333.       default:
  334.         printf ("TLC1ATTR: ERROR - default feature\n");
  335.         break;
  336.       }
  337.     }
  338.  
  339.     /* if it is a direction segment, write at proper location */
  340.     else
  341.       tlc1track (&xTrack, &yTrack, &attrVar, codeWord);
  342.   }
  343.  
  344.   printf ("TLC1ATTR: ERROR - missing STOPCODE in PCC storage \n");
  345.  
  346.   return (0);
  347. }
  348.  
  349.  
  350.  
  351. /* TLC1TRACK:   function decodes codeword and tracks directions,
  352.  *            keeping track of line length (where length is measured
  353.  *              in units of 10 * pixel spacing: 10 for vert/horiz, 
  354.  *              14 for diagonal
  355.  *                      usage: nDirns = tlc1track (&x, &y, &attrVar, codeWord)
  356.  */
  357.  
  358. extern int deCode[NDECODE];     /* decode table */
  359.  
  360. #define DECODE(BITSHIFT) \
  361.         dirn = ((deCode[codeWord] >> BITSHIFT) & 07) + 1; \
  362.         attrVar->length += ((dirn % 2) == 0) ? 14:10; \
  363.         nbrtoxy (dirn, *x, *y, x, y); \
  364.         if (*x < attrVar->box.min.x) attrVar->box.min.x = *x; \
  365.         else if (*x > attrVar->box.max.x) attrVar->box.max.x = *x; \
  366.         if (*y < attrVar->box.min.y) attrVar->box.min.y = *y; \
  367.         else if (*y > attrVar->box.max.y) attrVar->box.max.y = *y; \
  368.           attrVar->sumPt.x += *x; \
  369.           attrVar->sumPt.y += *y; \
  370.           (attrVar->nPts)++; \
  371.           if (*x < attrVar->box.min.x) attrVar->box.min.x = *x; \
  372.           if (*y < attrVar->box.min.y) attrVar->box.min.y = *y; \
  373.           if (*x > attrVar->box.max.x) attrVar->box.max.x = *x; \
  374.           if (*y > attrVar->box.max.y) attrVar->box.max.y = *y
  375.  
  376. tlc1track (x, y, attrVar, codeWord)
  377.      long *x, *y;               /* input/output line coordinates */
  378.      struct attrVar *attrVar;   /* variables to calculate level 1 attr.s */
  379.      register int codeWord;     /* code word contains up to 3 directions */
  380. {
  381.   register long dirn;           /* direction of next segment */
  382.  
  383. /* 3 direction segments per code word */
  384.   if (MINCODE3 <= codeWord && MAXCODE3 >= codeWord) {
  385.     DECODE (0);
  386.     DECODE (3);
  387.     DECODE (6);
  388.     return (3);
  389.   }
  390.  
  391. /* 2 direction segments per code word */
  392.   if (MINCODE2 <= codeWord && MAXCODE2 >= codeWord) {
  393.     DECODE (0);
  394.     DECODE (3);
  395.     return (2);
  396.   }
  397.  
  398. /* 1 direction segment per code word */
  399.   if (MINCODE1 <= codeWord && MAXCODE1 >= codeWord) {
  400.     DECODE (0);
  401.     return (1);
  402.   }
  403.  
  404. /* 0 direction segment per code word */
  405.   if (codeWord == CODE0) {
  406.     return (0);
  407.   }
  408. }
  409.  
  410.  
  411.  
  412. /* TLC1DIRN:    function calculates directions of ends of line segment
  413.  *            from list of line segments. The length in samples
  414.  *              on which the direction measurements are made is given.
  415.  *              The directions are calculated as endpoint fits, and
  416.  *              quantized to [degrees/2].
  417.  *                      usage: tlc1dirn (&attr, nAttr)
  418.  *
  419.  */
  420.  
  421. /* segment length upon which directions are calculated [pixels * 10] */
  422. #define DIRNLENGTH 100
  423.  
  424. tlc1dirn (attr, nAttr)
  425.      struct attributes *attr;   /* level 1 attributes */
  426.      long nAttr;                /* no. attributed lines in attr[] */
  427. {
  428.   register long iAttr,          /* attribute list incrementor */
  429.     iCode,                      /* code incrementor */
  430.     iCodeBegin,                 /* begin and end of segment in code */
  431.     iCodeEnd;
  432.   long deltaX,                  /* x,y increment of segment slope */
  433.     deltaY;
  434.   long dirncalc ();             /* given x,y increments, returns direction */
  435.   struct attrVar attrVar;       /* variables to calculate level 1 attr.s */
  436.  
  437.   for (iAttr = 0; iAttr < nAttr; iAttr++) {
  438.     /* calculate beginning and end of code for line segment */
  439.  
  440.     iCodeBegin = attr[iAttr].iByteCode;
  441.  
  442.     if (iAttr == nAttr - 1)     /* STOPCODE */
  443.       iCodeEnd = nByteCode - 2;
  444.     else {
  445.       if (attr[iAttr + 1].inOut > 0 && attr[iAttr + 1].inOut <= 4)
  446.         /* source node */
  447.         iCodeEnd = attr[iAttr + 1].iByteCode - 6;
  448.       else                      /* not source node */
  449.         iCodeEnd = attr[iAttr + 1].iByteCode - 2;
  450.     }
  451.  
  452.     /* find directions at beginning and end of line segment */
  453.  
  454.     deltaX = deltaY = attrVar.length = 0;
  455.     for (iCode = iCodeBegin;
  456.          (iCode <= iCodeEnd) && (attrVar.length < DIRNLENGTH);
  457.          iCode++)
  458.       tlc1track (&deltaX, &deltaY, &attrVar, (long) fcCode[iCode]);
  459.     attr[iAttr].dirn1 = (dirncalc (deltaX, deltaY) + 90) % 180;
  460.  
  461.     deltaX = deltaY = attrVar.length = 0;
  462.     for (iCode = iCodeEnd;
  463.          (iCode >= iCodeBegin) && (attrVar.length < DIRNLENGTH);
  464.          iCode--)
  465.       tlc1track (&deltaX, &deltaY, &attrVar, fcCode[iCode]);
  466.     attr[iAttr].dirn2 = (unsigned char) dirncalc (deltaX, deltaY);
  467.  
  468.   }
  469.   return (0);
  470. }
  471.  
  472. /* DIRNCALC: function calculates directions from x,y increments
  473.  *         Directions are in [degrees/2].
  474.  *                      usage: direction = dirncalc (deltaX, deltaY)
  475.  *                                      long direction;
  476.  */
  477. #include <math.h>
  478.  
  479. #define NODIRN 180              /* no direction */
  480. #if defined(WIN32)
  481. #define PI 3.1415927
  482. #endif
  483.  
  484. long
  485. dirncalc (deltaX, deltaY)
  486.      long deltaX, deltaY;       /* x, y increments */
  487.  
  488. {
  489.   register long direction;      /* resultant direction */
  490.  
  491.   double slope;                 /* slope of segment */
  492.   double atan ();
  493.  
  494. /* find directions associated with slopes */
  495.  
  496.   if (deltaX == 0) {
  497.     if (deltaY > 0)
  498.       return (45);
  499.     if (deltaY < 0)
  500.       return (135);
  501.     if (deltaY == 0)
  502.       return (NODIRN);
  503.   }
  504.  
  505.   slope = ((double) deltaY) / ((double) deltaX);
  506.   direction = (long) (atan (slope) * 180.0 / PI);
  507.  
  508.   if (deltaX < 0)
  509.     direction += 180;
  510.   direction = (direction + 360) % 360;
  511.   direction /= 2;
  512.  
  513.   return (direction);
  514. }
  515.  
  516.  
  517.  
  518.  /* TLC1STORE:  function takes variables associated with the
  519.   * *           line, calculates line attributes, and stores them
  520.   * *                   usage: tlc1store (attrVar, attr, &nAttr)
  521.   * *
  522.   * *           Note that two attributes are stored as approximations 
  523.   * *           for the sake of coding and computation ease. The line
  524.   * *           length ending at a branch or a cross is in error by 0.2
  525.   * *           pixel units (that is, the average difference between
  526.   * *           a diagonal and vert/horiz length). Line lengths
  527.   * *           beginning at a branch or cross have the same approximation.
  528.   * *           Line lengths beginning and ending at a cross or branch
  529.   * *           have maximum error of 0.4. For the above conditions,
  530.   * *           the end directions are also approximate. The last
  531.   * *           vector in the 3 vector code is actually beyond the
  532.   * *           branch or cross point, but is used in the direction
  533.   * *           determination for the line end at this point.
  534.   * *
  535.   */
  536.  
  537. #define MOD8(A,B) ((((A) + (B) - 1) % 8) + 1)
  538.     /* mod 8 addition, where result = [1,8] */
  539.  
  540. tlc1store (attrVar, attr, nAttr)
  541.      struct attrVar attrVar;    /* variables to calculate level 1 attr.s */
  542.      struct attributes *attr;   /* level 1 attributes */
  543.      long *nAttr;               /* no. attributed lines in attr[] */
  544. {
  545.  
  546. /* line type */
  547.   switch (attrVar.feat1) {
  548.   case STARTCODE:
  549.     switch (attrVar.feat2) {
  550.     case ENDCODE:
  551.       attr[*nAttr].type = EELINE;
  552.       break;
  553.     case BIFCODE:
  554.       attr[*nAttr].type = EBLINE;
  555.       break;
  556.     case CROSSCODE:
  557.       attr[*nAttr].type = ECLINE;
  558.       break;
  559.     default:
  560.       printf ("TLC1STORE: error\n");
  561.       break;
  562.     }
  563.     break;
  564.  
  565.   case LINEBRCODE:
  566.     switch (attrVar.feat2) {
  567.     case ENDCODE:
  568.       attr[*nAttr].type = RLELINE;
  569.       break;
  570.     case BIFCODE:
  571.       attr[*nAttr].type = RLBLINE;
  572.       break;
  573.     case CROSSCODE:
  574.       attr[*nAttr].type = RLCLINE;
  575.       break;
  576.     default:
  577.       printf ("TLC1STORE: error\n");
  578.       break;
  579.     }
  580.     break;
  581.  
  582.   case BIFCODE:
  583.     switch (attrVar.feat2) {
  584.     case ENDCODE:
  585.       attr[*nAttr].type = BELINE;
  586.       break;
  587.     case BIFCODE:
  588.       attr[*nAttr].type = BBLINE;
  589.       break;
  590.     case CROSSCODE:
  591.       attr[*nAttr].type = BCLINE;
  592.       break;
  593.     default:
  594.       printf ("TLC1STORE: error\n");
  595.       break;
  596.     }
  597.     break;
  598.  
  599.   case BIFBRCODE:
  600.     switch (attrVar.feat2) {
  601.     case ENDCODE:
  602.       attr[*nAttr].type = RBELINE;
  603.       break;
  604.     case BIFCODE:
  605.       attr[*nAttr].type = RBBLINE;
  606.       break;
  607.     case CROSSCODE:
  608.       attr[*nAttr].type = RBCLINE;
  609.       break;
  610.     default:
  611.       printf ("TLC1STORE: error\n");
  612.       break;
  613.     }
  614.     break;
  615.  
  616.   case CROSSCODE:
  617.     switch (attrVar.feat2) {
  618.     case ENDCODE:
  619.       attr[*nAttr].type = CELINE;
  620.       break;
  621.     case BIFCODE:
  622.       attr[*nAttr].type = CBLINE;
  623.       break;
  624.     case CROSSCODE:
  625.       attr[*nAttr].type = CCLINE;
  626.       break;
  627.     default:
  628.       printf ("TLC1STORE: error\n");
  629.       break;
  630.     }
  631.     break;
  632.  
  633.   case CROSSBRCODE:
  634.     switch (attrVar.feat2) {
  635.     case ENDCODE:
  636.       attr[*nAttr].type = RCELINE;
  637.       break;
  638.     case BIFCODE:
  639.       attr[*nAttr].type = RCBLINE;
  640.       break;
  641.     case CROSSCODE:
  642.       attr[*nAttr].type = RCCLINE;
  643.       break;
  644.     default:
  645.       printf ("TLC1STORE: error\n");
  646.       break;
  647.     }
  648.     break;
  649.  
  650.   default:
  651.     printf ("TLC1STORE: not end, branch, or cross -- Problems\n");
  652.   }
  653.  
  654. /* location in <fcCode[]> of this attributed line 
  655.  * Note that this is the line beginning location - i.e. after
  656.  * the stored codes for: feature, x, y */
  657.  
  658.   attr[*nAttr].iByteCode = attrVar.iByteCode;
  659.  
  660. /* branches to/from beginning of line, where +10 for input to
  661.  * beginning of line, +1 for output from beginning of line */
  662.  
  663.   attr[*nAttr].inOut = attrVar.inOut;
  664.  
  665. /* pixel length of line: horiz/vert = 10, diag length = 14
  666.  * See note regarding approximate lengths in heading */
  667.  
  668.   attr[*nAttr].length = attrVar.length;
  669.  
  670. /* start and end image coordinates */
  671.  
  672.   attr[*nAttr].pt1.x = attrVar.pt1.x;
  673.   attr[*nAttr].pt1.y = attrVar.pt1.y;
  674.   attr[*nAttr].pt2.x = attrVar.pt2.x;
  675.   attr[*nAttr].pt2.y = attrVar.pt2.y;
  676.  
  677. /* sum and bounding box info */
  678.   attr[*nAttr].box = attrVar.box;
  679.   attr[*nAttr].sumPt = attrVar.sumPt;
  680.   attr[*nAttr].nPts = attrVar.nPts;
  681.  
  682.   (*nAttr)++;
  683.  
  684.   return (0);
  685. }
  686.  
  687.  
  688.  
  689. /* TLC1TAG:     function cleans up line ends, tagging them for removal if
  690.  *            their length is below minimum tolerance
  691.  *                      usage:  tlc1tag (attr, nAttr, minEE, minFE)
  692.  *
  693.  *              Note:   There is a little bit of ugly protocol in here
  694.  *                      which needs to be explained.
  695.  *                      The array, <attr[].inOut> contains the number of
  696.  *                      branches leaving a node... sort of. That is,
  697.  *                      for each branch leaving a node, +1 is added
  698.  *                      to that variable; if a branch goes into that
  699.  *                      node, +10 is added. (There can be only 0 or 
  700.  *                      1 branches into a node.) The variable is 
  701.  *                      associated with the FIRST node of a line
  702.  *                      feature (e.g. the feature, CB has 1 entering
  703.  *                      the first node, and 3 leaving the first node,
  704.  *                      therefore the variable =10 + 3 = 13.)  For the
  705.  *                      attr[] array, the FIRST OUTGOING branch of each
  706.  *                      junction carries information of the junction.
  707.  *                      For example, {EB, BE, BE} have stored inOut,
  708.  *                      {1, 12, 0}. 
  709.  *
  710.  */
  711.  
  712. tlc1tag (attr, nAttr, minEE, minFE)
  713.      struct attributes *attr;   /* level 1 attributes */
  714.      register long nAttr;       /* no. of line structures in attr. array */
  715.      long minEE, minFE;         /* minimum EE and FE line thresholds */
  716. {
  717.   register long iAttr,          /* incrementor for attributes */
  718.     iBranchBreak;               /* down counter for no. of branches from
  719.                                  * * break, not already tested */
  720.  
  721.   struct branch1 *branch;       /* stack for branches */
  722.   struct branch1 branchTop;     /* first branch */
  723.   long nBranch;                 /* no. of branches in stack */
  724.  
  725. /* initialize */
  726.   branch = &branchTop;
  727.   branch->previous = branch;
  728.   iBranchBreak = 0;
  729.   nBranch = 0;
  730.  
  731. /* test each line feature */
  732.  
  733.   for (iAttr = 0; iAttr < nAttr; iAttr++) {
  734.  
  735.     switch (attr[iAttr].type) {
  736.  
  737.     case EELINE:
  738.       if (attr[iAttr].length < minEE) {
  739.         attr[iAttr].type *= -1;
  740.         attr[iAttr].inOut -= 1;
  741.       }
  742.       break;
  743.  
  744.     case ECLINE:
  745.       pccbranch (&branch, 0, 0, iAttr + 1);
  746.       nBranch++;
  747.  
  748.     case EBLINE:
  749.       pccbranch (&branch, 0, 0, iAttr + 1);
  750.       nBranch++;
  751.       pccbranch (&branch, 0, 0, iAttr + 1);
  752.       nBranch++;
  753.       if (attr[iAttr].length < minFE) {
  754.         attr[iAttr].type *= -1;
  755.         attr[iAttr].inOut -= 1;
  756.         attr[iAttr + 1].inOut -= 10;
  757.       }
  758.       break;
  759.  
  760.     case BELINE:
  761.     case CELINE:
  762.       nBranch--;
  763.       branch = branch->previous;
  764.       if (attr[iAttr].length < minFE
  765.           && attr[iAttr].length > 0
  766.           && looptest (attr, iAttr, branch, nBranch) == 0) {
  767.         attr[iAttr].type *= -1;
  768.         if (iAttr == branch->variable)
  769.           attr[iAttr].inOut -= 1;
  770.         else
  771.           attr[branch->variable].inOut -= 1;
  772.       }
  773.       break;
  774.  
  775.     case BBLINE:
  776.     case CBLINE:
  777.       nBranch--;
  778.       branch = branch->previous;
  779.       pccbranch (&branch, 0, 0, iAttr + 1);
  780.       nBranch++;
  781.       pccbranch (&branch, 0, 0, iAttr + 1);
  782.       nBranch++;
  783.       break;
  784.  
  785.     case BCLINE:
  786.     case CCLINE:
  787.       nBranch--;
  788.       branch = branch->previous;
  789.       pccbranch (&branch, 0, 0, iAttr + 1);
  790.       nBranch++;
  791.       pccbranch (&branch, 0, 0, iAttr + 1);
  792.       nBranch++;
  793.       pccbranch (&branch, 0, 0, iAttr + 1);
  794.       nBranch++;
  795.       break;
  796.  
  797.     case RBELINE:
  798.       /* if first (and only) break in structure, push branches */
  799.       if (iBranchBreak == 0) {  /* first feature in break */
  800.         iBranchBreak = 3;       /* 3 branches */
  801.         pccbranch (&branch, 0, 0, iAttr);
  802.         nBranch++;
  803.         pccbranch (&branch, 0, 0, iAttr);
  804.         nBranch++;
  805.         pccbranch (&branch, 0, 0, iAttr);
  806.         nBranch++;
  807.       }
  808.       iBranchBreak--;
  809.       nBranch--;
  810.       branch = branch->previous;
  811.       if (attr[iAttr].length < minFE
  812.           && attr[iAttr].length > 0
  813.           && looptest (attr, iAttr, branch, nBranch) == 0) {
  814.         attr[iAttr].type *= -1;
  815.         if (iAttr == branch->variable)
  816.           attr[iAttr].inOut -= 1;
  817.         else
  818.           attr[branch->variable].inOut -= 1;
  819.       }
  820.       break;
  821.  
  822.     case RCELINE:
  823.       /* if first (and only) break in structure, push branches */
  824.       if (iBranchBreak == 0) {
  825.         iBranchBreak = 4;
  826.         pccbranch (&branch, 0, 0, iAttr);
  827.         nBranch++;
  828.         pccbranch (&branch, 0, 0, iAttr);
  829.         nBranch++;
  830.         pccbranch (&branch, 0, 0, iAttr);
  831.         nBranch++;
  832.         pccbranch (&branch, 0, 0, iAttr);
  833.         nBranch++;
  834.       }
  835.       iBranchBreak--;
  836.       nBranch--;
  837.       branch = branch->previous;
  838.       if (attr[iAttr].length < minFE
  839.           && attr[iAttr].length > 0
  840.           && looptest (attr, iAttr, branch, nBranch) == 0) {
  841.         attr[iAttr].type *= -1;
  842.         if (iAttr == branch->variable)
  843.           attr[iAttr].inOut -= 1;
  844.         else
  845.           attr[branch->variable].inOut -= 1;
  846.       }
  847.       break;
  848.  
  849.     case RLELINE:
  850.     case RLBLINE:
  851.     case RLCLINE:
  852.       /* if first break (of pair), push it and continue */
  853.       if (iBranchBreak == 0) {
  854.         iBranchBreak = 1;
  855.         pccbranch (&branch, 0, 0, iAttr);
  856.         nBranch++;
  857.       }
  858.       else {
  859.         iBranchBreak--;
  860.         nBranch--;
  861.         branch = branch->previous;
  862.         /* if single line (endpt at each end) */
  863.         if (
  864.              (attr[iAttr].type == RLELINE && attr[iAttr].length != 0)
  865.              && (attr[branch->variable].type == RLELINE
  866.                  && attr[branch->variable].length != 0)) {
  867.           if ((attr[iAttr].length
  868.                + attr[branch->variable].length)
  869.               < minEE
  870.               && looptest (attr, iAttr, branch, nBranch) == 0) {
  871.             attr[iAttr].type *= -1;
  872.             attr[branch->variable].type *= -1;
  873.             attr[branch->variable].inOut -= 2;
  874.           }
  875.         }
  876.         /* if line attached to rest of structure at one end */
  877.         else if (attr[iAttr].type == RLELINE
  878.                  || attr[branch->variable].type
  879.                  == RLELINE) {
  880.  
  881.           if (attr[iAttr].length != 0 &&
  882.               attr[branch->variable].length != 0) {
  883.  
  884.             if ((attr[iAttr].length
  885.                  + attr[branch->variable].length)
  886.                 < minFE
  887.                 && looptest (attr, iAttr, branch, nBranch) == 0) {
  888.               /* change following feature first, if need be */
  889.               if (attr[iAttr].type == RLBLINE
  890.                   || attr[iAttr].type == RLCLINE)
  891.                 attr[iAttr + 1].inOut -= 10;
  892.               else if (attr[branch->variable].type
  893.                        == RLBLINE
  894.                        || attr[branch->variable].type
  895.                        == RLCLINE)
  896.                 attr[branch->variable + 1].inOut
  897.                   -= 10;
  898.  
  899.               attr[iAttr].type *= -1;
  900.               attr[branch->variable].type *= -1;
  901.               attr[branch->variable].inOut -= 2;
  902.             }
  903.           }
  904.         }
  905.       }
  906.       if (attr[iAttr].type == RLBLINE
  907.           || attr[iAttr].type == -RLBLINE) {
  908.         pccbranch (&branch, 0, 0, iAttr + 1);
  909.         nBranch++;
  910.         pccbranch (&branch, 0, 0, iAttr + 1);
  911.         nBranch++;
  912.       }
  913.       if (attr[iAttr].type == RLCLINE
  914.           || attr[iAttr].type == -RLCLINE) {
  915.         pccbranch (&branch, 0, 0, iAttr + 1);
  916.         nBranch++;
  917.         pccbranch (&branch, 0, 0, iAttr + 1);
  918.         nBranch++;
  919.         pccbranch (&branch, 0, 0, iAttr + 1);
  920.         nBranch++;
  921.       }
  922.  
  923.       break;
  924.  
  925.     case RBBLINE:
  926.       /* if first (and only) break in structure, push branches */
  927.       if (iBranchBreak == 0) {
  928.         iBranchBreak = 3;
  929.         pccbranch (&branch, 0, 0, iAttr);
  930.         nBranch++;
  931.         pccbranch (&branch, 0, 0, iAttr);
  932.         nBranch++;
  933.         pccbranch (&branch, 0, 0, iAttr);
  934.         nBranch++;
  935.       }
  936.       iBranchBreak--;
  937.       nBranch--;
  938.       branch = branch->previous;
  939.  
  940.       pccbranch (&branch, 0, 0, iAttr + 1);
  941.       nBranch++;
  942.       pccbranch (&branch, 0, 0, iAttr + 1);
  943.       nBranch++;
  944.       break;
  945.  
  946.     case RBCLINE:
  947.       /* if first (and only) break in structure, push branches */
  948.       if (iBranchBreak == 0) {
  949.         iBranchBreak = 3;
  950.         pccbranch (&branch, 0, 0, iAttr);
  951.         nBranch++;
  952.         pccbranch (&branch, 0, 0, iAttr);
  953.         nBranch++;
  954.         pccbranch (&branch, 0, 0, iAttr);
  955.         nBranch++;
  956.       }
  957.       iBranchBreak--;
  958.       nBranch--;
  959.       branch = branch->previous;
  960.  
  961.       pccbranch (&branch, 0, 0, iAttr + 1);
  962.       nBranch++;
  963.       pccbranch (&branch, 0, 0, iAttr + 1);
  964.       nBranch++;
  965.       pccbranch (&branch, 0, 0, iAttr + 1);
  966.       nBranch++;
  967.       break;
  968.  
  969.     case RCBLINE:
  970.       /* if first (and only) break in structure, push branches */
  971.       if (iBranchBreak == 0) {
  972.         iBranchBreak = 4;
  973.         pccbranch (&branch, 0, 0, iAttr);
  974.         nBranch++;
  975.         pccbranch (&branch, 0, 0, iAttr);
  976.         nBranch++;
  977.         pccbranch (&branch, 0, 0, iAttr);
  978.         nBranch++;
  979.         pccbranch (&branch, 0, 0, iAttr);
  980.         nBranch++;
  981.       }
  982.       iBranchBreak--;
  983.       nBranch--;
  984.       branch = branch->previous;
  985.  
  986.       pccbranch (&branch, 0, 0, iAttr + 1);
  987.       nBranch++;
  988.       pccbranch (&branch, 0, 0, iAttr + 1);
  989.       nBranch++;
  990.       break;
  991.  
  992.     case RCCLINE:
  993.       /* if first (and only) break in structure, push branches */
  994.       if (iBranchBreak == 0) {
  995.         iBranchBreak = 4;
  996.         pccbranch (&branch, 0, 0, iAttr);
  997.         nBranch++;
  998.         pccbranch (&branch, 0, 0, iAttr);
  999.         nBranch++;
  1000.         pccbranch (&branch, 0, 0, iAttr);
  1001.         nBranch++;
  1002.         pccbranch (&branch, 0, 0, iAttr);
  1003.         nBranch++;
  1004.       }
  1005.       iBranchBreak--;
  1006.       nBranch--;
  1007.       branch = branch->previous;
  1008.  
  1009.       pccbranch (&branch, 0, 0, iAttr + 1);
  1010.       nBranch++;
  1011.       pccbranch (&branch, 0, 0, iAttr + 1);
  1012.       nBranch++;
  1013.       pccbranch (&branch, 0, 0, iAttr + 1);
  1014.       nBranch++;
  1015.       break;
  1016.  
  1017.     default:
  1018.       printf ("TLC1TAG: error default type\n");
  1019.       break;
  1020.     }
  1021.   }
  1022.   return (0);
  1023. }
  1024.  
  1025.  
  1026. /* LOOPTEST:    function tests to see if current end line is end of loop
  1027.  *                    usage: loop = looptest (attr, iAttr, branch, nBranch)
  1028.  */
  1029.  
  1030. int
  1031. looptest (attr, iAttr, branch, nBranch)
  1032.      struct attributes *attr;   /* level 1 attributes */
  1033.      register long iAttr;       /* current line no. of endline to be tested */
  1034.      struct branch1 *branch;    /* stack for branches */
  1035.      long nBranch;              /* no. of branches in stack */
  1036. {
  1037.   register long iBranch,        /* branch stack incrementor */
  1038.     xDist,                      /* x,y distances of endpt. from branch pt */
  1039.     yDist;
  1040.   struct branch1 *pBranch;      /* pointer to branch stack */
  1041.  
  1042. /* test through all pushed branches whether current endpointis end of loop */
  1043.   pBranch = branch;
  1044.   for (iBranch = nBranch - 1; iBranch >= 0; iBranch--) {
  1045.     pBranch = pBranch->previous;
  1046.     if (pBranch->variable != iAttr) {
  1047.       xDist = attr[iAttr].pt2.x - attr[pBranch->variable].pt1.x;
  1048.       yDist = attr[iAttr].pt2.y - attr[pBranch->variable].pt1.y;
  1049.       xDist = (xDist < 0) ? -xDist : xDist;
  1050.       yDist = (yDist < 0) ? -yDist : yDist;
  1051.       if (xDist <= 1 && yDist <= 1)
  1052.         return (1);
  1053.     }
  1054.   }
  1055.   return (0);
  1056. }
  1057.  
  1058.  
  1059.  
  1060. /* TLC1RID:     function edits fcc code, removing lines and modifying
  1061.  *            feature codes on basis of array of junction
  1062.  *              branches and lines to be omitted. Function returns
  1063.  *              number of code bytes after ridding lines.
  1064.  *                      usage: nByteCode = tlc1rid (attr, nAttr)
  1065.  */
  1066.  
  1067. #define NFEATJCTS 14            /* max. no. in featJcts array */
  1068.  
  1069. tlc1rid (attr, nAttr)
  1070.      struct attributes *attr;   /* level 1 attributes */
  1071.      register long nAttr;       /* no. of line structures in attr. array */
  1072. {
  1073.   long featJcts[NFEATJCTS],     /* array positions are junction numbers
  1074.                                  * * corresponding to node feature codes */
  1075.     source,                     /* flag: (=1) => stored source point,
  1076.                                  * * (=0) => no stored source point */
  1077.     nBreakPerStruct;            /* no. breaks remaining in current struct
  1078.                                  * * (if new break, then source of structure) */
  1079.   register long iAttr,          /* incrementor for attributes */
  1080.     nByteFeatM1,                /* no. bytes in fcc for (feat, x, y) - 1 */
  1081.     iCodeIn,                    /* incrementor for read input fcc */
  1082.     iCodeOut,                   /* incrementor for write output fcc */
  1083.     type;                       /* absolute value of line type */
  1084.   unsigned char *fcCodeOut;     /* ptr to output fcc written over input */
  1085.  
  1086. /* initialize */
  1087.   iCodeOut = 0;
  1088.   source = 0;
  1089.   nBreakPerStruct = 0;
  1090.   fcCodeOut = fcCode;
  1091.   nByteFeatM1 = 1 + 2 * 2 - 1;
  1092.  
  1093. /* initialize array of features corresponding to junction branches,
  1094.  * where entering branch has value of (+10), exiting branches (+1) each */
  1095.   featJcts[1] = STARTCODE;
  1096.   featJcts[2] = LINEBRCODE;
  1097.   featJcts[3] = BIFBRCODE;
  1098.   featJcts[4] = CROSSBRCODE;
  1099.   featJcts[10] = ENDCODE;
  1100.   featJcts[11] = LINECODE;
  1101.   featJcts[12] = BIFCODE;
  1102.   featJcts[13] = CROSSCODE;
  1103.  
  1104. /* test for each line */
  1105.   for (iAttr = 0; iAttr < nAttr; iAttr++) {
  1106.  
  1107.     /* for original code of start or break, set <source> flag */
  1108.     type = ABS (attr[iAttr].type);
  1109.     if (type >= EELINE && type <= ECLINE)
  1110.       source = 1;
  1111.     else if (type >= RLELINE && type <= RCCLINE) {
  1112.       if (nBreakPerStruct == 0) {
  1113.         source = 1;
  1114.         if (type >= RLELINE && type <= RLCLINE)
  1115.           nBreakPerStruct = 1;
  1116.         else if (type >= RBELINE && type <= RBCLINE)
  1117.           nBreakPerStruct = 2;
  1118.         else if (type >= RCELINE && type <= RCCLINE)
  1119.           nBreakPerStruct = 3;
  1120.       }
  1121.       else {
  1122.         nBreakPerStruct -= 1;
  1123.       }
  1124.     }
  1125.  
  1126.     /* for what is now: a start or break code, store (feature, x, y);
  1127.      * another feature type, store (feature); 
  1128.      * a linebreak or nothing, store no feature 
  1129.      * If there is no stored source point (source = 0),
  1130.      * then next line is a branch from a previous junction */
  1131.  
  1132.     if (attr[iAttr].inOut > 0 && attr[iAttr].inOut <= 4) {
  1133.       if (source == 1) {
  1134.         fcCodeOut[iCodeOut++] =
  1135.           (unsigned char) featJcts[attr[iAttr].inOut];
  1136.         fcCode[iCodeOut++]
  1137.           = (unsigned char) (attr[iAttr].pt1.x & 0xff);
  1138.         fcCode[iCodeOut++]
  1139.           = (unsigned char) ((attr[iAttr].pt1.x >> 8) & 0xff);
  1140.         fcCode[iCodeOut++]
  1141.           = (unsigned char) (attr[iAttr].pt1.y & 0xff);
  1142.         fcCode[iCodeOut++]
  1143.           = (unsigned char) ((attr[iAttr].pt1.y >> 8) & 0xff);
  1144.         source = 0;
  1145.       }
  1146.     }
  1147.     else if (attr[iAttr].inOut >= 10 && attr[iAttr].inOut != 11)
  1148.       fcCodeOut[iCodeOut++] =
  1149.         (unsigned char) featJcts[attr[iAttr].inOut];
  1150.     else if (attr[iAttr].inOut == 0 || attr[iAttr].inOut == 11);
  1151.     else
  1152.       printf ("TLC1RID: ERROR: attr[%3d].inOut = %3d\n", iAttr,
  1153.               attr[iAttr].inOut);
  1154.  
  1155.     /* if line not tagged to be eliminated, then read/write until feature */
  1156.  
  1157.     iCodeIn = attr[iAttr].iByteCode;
  1158.     if (attr[iAttr].type > 0) {
  1159.       for (; fcCode[iCodeIn] < MINFEATCODE; iCodeIn++)
  1160.         fcCodeOut[iCodeOut++] = fcCode[iCodeIn];
  1161.  
  1162.       if (fcCode[iCodeIn] == ENDCODE)
  1163.         fcCodeOut[iCodeOut++] = fcCode[iCodeIn];
  1164.     }
  1165.   }
  1166.  
  1167. /* write stop code */
  1168.   fcCodeOut[iCodeOut++] = STOPCODE;
  1169.  
  1170.   return (iCodeOut);
  1171. }
  1172.